package mysgoog.bot;

import java.util.*;
import mysgoog.MC.*;
import mysgoog.board.*;
import mysgoog.bot.algo.*;
import mysgoog.gtp.*;
import mysgoog.pattern.*;
import mysgoog.pattern.Storage.VertexValue;
import mysgoog.util.*;

public class FinalBot extends GoBot
{
    private DragonBoard board;
    private PatternMatcher ptnMatcher = null;

    @Override
    public void setBoard(Board board)
    {
        this.board = (DragonBoard) board;
    }
    private ValuedMap collectDefs(Color color, HashSet<Dragon> nearDragons)
    {
        ValuedMap res = new ValuedMap();
        for (Dragon dragon : nearDragons)
            if (dragon.color == color && dragon.isDangerous())
            {
                for (Worm worm : dragon)
                    if (worm.getLibs().size() <= 3)
                    {
                        LinkedList<SearchResult> srs = AdvanceSearch.Defend(board, color,
                                worm.getOne());
                        for (SearchResult sr : srs)
                            if (board.isLegal(color, sr.getPos()) && checkEyes(color, sr.getPos())
                                    && new Evaluator(board).easyEvaluate(sr.getPos(), color))
                            {
                                double val = worm.size() * 2 * board.getPosLib(sr.getPos())
                                        * Math.pow(1.0 / worm.getLibs().size(), 3);
                                if (sr.getGrade() == Evaluator.MID)
                                    val *= 0.1;
                                if (sr.getGrade() == Evaluator.LOW
                                        || dragon.getEyes().contains(sr.getPos())
                                        || board.isEdge(sr.getGrade()))
                                    val *= 0.01;
                                res.add(sr.getPos(), val);
                            }
                    }
            }
        return res;
    }
    private ValuedMap collectAtks(Color color, HashSet<Dragon> nearDragons)
    {
        ValuedMap res = new ValuedMap();
        LinkedList<SearchResult> srs = AdvanceSearch.Capture(board, color);
        for (SearchResult sr : srs)
            if (board.isLegal(color, sr.getPos()) && checkAllEyes(color, sr.getPos()))
                for (int wormPos : sr.getWorms())
                {
                    Worm worm = board.wormAt(wormPos);
                    Dragon dragon = board.dragonAt(wormPos);
                    Lib.assertTrue(worm != null, new Vertex(wormPos).toString());
                    if (board.getAdjacent(sr.getPos(), worm).size() == 0)
                        continue;
                    double val = dragon.size() * board.getPosLib(sr.getPos());
                    if (nearDragons.contains(dragon))
                        val *= 2;
                    if (sr.getGrade() == Evaluator.LOW)
                        val *= 0.01;
                    res.add(sr.getPos(), val);
                }
        return res;
    }
    @Override
    public Vertex genMove(Color color)
    {
        if (ptnMatcher == null)
        {
            try
            {
                ptnMatcher = new PatternMatcher();
            } catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        if (board.getTurn() <= 20)
        {
            Vertex open = openning(color);
            if (open != null)
            {
                Lib.debug("[open]");
                return open;
            }
        }
        // 9 grids
        if (board.getTurn() <= 30)
        {
            int balpos = Balance.balance(board, color);
            // System.out.println(balpos);
            if (balpos >= 0)
            {
                MCManager mc = new MCManager(color, board, balpos);
                Vertex ret = mc.MCGen();
                int pos = board.makePos(ret.x, ret.y);
                if (checkAlive(color, pos) && checkAllEyes(color, pos))
                {
                    Lib.debug("[MC 9grids]");
                    return ret;
                }
            }
        }
        // makeeye pattern
        if (board.getTurn() >= 120)
        {
            List<VertexValue> pattern = this.ptnMatcher.getMakeeyePatternMatch(board, color);
            if (pattern.size() > 0)
                for (VertexValue vv : pattern)
                {
                    int pos = board.makePos(vv.vertex.x, vv.vertex.y);
                    if (board.isLegal(color, pos) && !board.isEdge(pos) && checkAllEyes(color, pos)
                            && new Evaluator(board).easyEvaluate(pos, color))
                    {
                        Lib.debug("[pattern]" + vv.value);
                        return vv.vertex;
                    }
                }
        }
        // live - dead
        HashSet<Dragon> nearDragons = new HashSet<Dragon>();
        if (board.getHistory().size() != 0)
        {
            int lastMove = board.getHistory().getLast().pos;
            for (int pos : board.getAround(lastMove, 2))
            {
                Dragon dragon = board.dragonAt(pos);
                if (dragon != null)
                    nearDragons.add(dragon);
            }
            if (board.getHistory().size() >= 2)
            {
                int myMove = board.getHistory().get(board.getHistory().size() - 2).pos;
                if (myMove != -1)
                {
                    Worm worm = board.wormAt(myMove);
                    if (worm != null && worm.getLibs().size() == 1)
                        nearDragons.add(board.dragonAt(myMove));
                }
            }
        }
        ValuedMap defs = collectDefs(color, nearDragons);
        ValuedMap atks = collectAtks(color, nearDragons);
        ValuedMap newMap = new ValuedMap();
        for (ValuedPos vp : defs.values())
            newMap.add(vp.pos, vp.val);
        for (ValuedPos vp : atks.values())
            newMap.add(vp.pos, vp.val);
        ValuedPos best = null;
        for (ValuedPos vp : newMap.values())
        {
            if (checkAlive(color, vp.pos) && (best == null || vp.val > best.val))
                best = vp;
        }
        if (best != null)
        {
            if (defs.containsKey(best.pos))
                Lib.debug("[def]");
            if (atks.containsKey(best.pos))
                Lib.debug("[atk]");
            return new Vertex(best.pos);
        }
        // explore empty area
        ArrayList<Integer> rndList = new ArrayList<Integer>();
        for (int pos = 0; pos < board.BOARD_ELEMNUM; pos++)
            if (!board.isEdge(pos) && !board.getStone(pos).isFilled() && board.disToEdge(pos) >= 2)
            {
                boolean ok = true;
                for (int a : board.getAround(pos))
                    if (board.getStone(a).isFilled())
                    {
                        ok = false;
                        break;
                    }
                if (ok)
                    rndList.add(pos);
            }
        if (rndList.size() > 0)
        {
            int pos = rndList.get(Lib.random(rndList.size()));
            if (!board.isLegal(color, pos))
                throw new Error("!!!");
            Lib.debug("[explore]");
            return new Vertex(pos);
        }
        // pattern
        List<VertexValue> pattern = this.ptnMatcher.getSmallPatternMatch(board, color);
        if (pattern.size() > 0)
            for (VertexValue vv : pattern)
            {
                int pos = board.makePos(vv.vertex.x, vv.vertex.y);
                if (board.isLegal(color, pos) && !board.isEdge(pos) && checkAllEyes(color, pos)
                        && new Evaluator(board).easyEvaluate(pos, color))
                {
                    Lib.debug("[pattern]" + vv.value);
                    return vv.vertex;
                }
            }
        // MC
        MCManager mcmanager = new MCManager(color, this.board);
        try
        {
            Vertex ret = mcmanager.MCGen();
            int pos = board.makePos(ret.x, ret.y);
            if (checkAlive(color, pos) && checkAllEyes(color, pos))
            {
                Lib.debug("[MC]");
                return ret;
            }
        } catch (Throwable e)
        {
            Lib.debug("!!!WARN!!! MC is down!");
        }

        // endgame
        pattern.clear();
        pattern = this.ptnMatcher.getEndGamePatternMatch(board, color);
        if (pattern.size() > 0)
            for (VertexValue vv : pattern)
            {
                int pos = board.makePos(vv.vertex.x, vv.vertex.y);
                if (board.isLegal(color, pos) && !board.isEdge(pos) && (checkAllEyes(color, pos)))
                {
                    Lib.debug("[endgame]");
                    return vv.vertex;
                }
            }
        // random
        rndList.clear();
        for (int pos = 0; pos <= board.BOARD_ELEMNUM; pos++)
            if (board.isLegal(color, pos) && checkAllEyes(color, pos))
            {
                rndList.add(pos);
            }
        if (rndList.size() > 0)
        {
            int pos = rndList.get(Lib.random(rndList.size()));
            if (!board.isLegal(color, pos))
                throw new Error("!!!");
            Lib.debug("[random]");
            return new Vertex(pos);
        }
        return new Vertex("pass");
    }
    private Vertex openning(Color color)
    {
        int star = checkStars();
        if (star != -1)
        {
            return new Vertex(board.posX(star), board.posY(star));
        }
        List<VertexValue> list = ptnMatcher.getMatch(board, color);
        if (list.size() > 0)
        {
            for (VertexValue vv : list)
                if (board.isLegal(color, board.makePos(vv.vertex.x, vv.vertex.y))
                        && checkAllEyes(color, board.makePos(vv.vertex.x, vv.vertex.y)))
                    return vv.vertex;
        }
        return null;
    }
    private int checkStars()
    {
        for (int pos : board.getStars())
            if (board.territoryAt(pos) == null)
                return pos;
        return -1;
    }
    private boolean checkEyes(Color color, int pos)
    {
        Dragon dragon = board.territoryAt(pos);
        if (dragon == null || dragon.color != color)
            return true;
        if (dragon.getEyes().contains(pos))
            return false;
        else
            return true;
    }
    private boolean checkAllEyes(Color color, int pos)
    {
        Dragon dragon = board.territoryAt(pos);
        if (dragon == null || dragon.color != color)
            return true;
        if (dragon.getEyes().contains(pos) || dragon.getHalfEyes().contains(pos))
            return false;
        else
            return true;
    }
    private boolean checkAlive(Color color, int pos)
    {
        if (!board.isLegal(color, pos))
            return false;
        boolean res = true;
        board.play(color, pos);
        if (board.wormAt(pos).getLibs().size() == 1
                && board.isLegal(color.complement(), board.wormAt(pos).getLibs().getOne()))
            res = false;
        board.undo();
        return res;

    }
}
